#pragma once
#include "cem/material_law.h"
#include "cem/material_types.h"

#include <array>
#include <memory>
#include <string>
#include <unordered_map>

namespace libMesh { class Elem; }

namespace cem
{

class ElectromagneticsSystem;
class ParameterReader; // 前向声明：避免头文件依赖环

// Material 表示一种材料的基础电磁属性（脚手架阶段字段有限）。
struct Material
{
    double epsilon{0.0};
    double mu{0.0};
    double sigma{0.0};
};

// 材料张量集合：集中存放一个子域的 σ、ε、μ^{-1}。
struct MaterialTensors
{
    std::array<std::array<double, 3>, 3> sigma{};
    std::array<std::array<double, 3>, 3> epsl{};
    std::array<std::array<double, 3>, 3> inv_mu{};
};

// MaterialDatabase 维护：
// 1) 名称到材料标量属性的映射（占位实现，供潜在数据库用）。
// 2) 集中式材料管理能力（原 MaterialManager 功能）：
//    - 组合 MaterialLaw 实例；
//    - 扫描网格子域并缓存张量；
//    - 对外提供 σ、ε、μ^{-1} 的统一访问接口。
class MaterialDatabase
{
public:
    MaterialDatabase() = default;

    // 函数: add_material
    // 作用: 向材料库添加一条记录（若重名则覆盖）。
    // 参数: name - 材料名, mat - 材料属性
    // 返回: 无
    void add_material(const std::string & name, const Material & mat);

    // 函数: find_material
    // 作用: 查找材料记录。
    // 参数: name - 材料名
    // 返回: 若存在返回常量指针，否则返回空指针
    const Material * find_material(const std::string & name) const;

    // --------- 区域映射（由参数读入后注入）---------
    // 函数: set_area_* / area_*_map
    // 作用: 设置/读取各区域的标量材料与各向异性规格映射。
    void set_area_epsl_map(const std::unordered_map<int, double> & m) { _area_epsl_map = m; }
    void set_area_cond_map(const std::unordered_map<int, double> & m) { _area_cond_map = m; }
    void set_area_mu_map(const std::unordered_map<int, double> & m) { _area_mu_map = m; }

    const std::unordered_map<int, double> & area_epsl_map() const { return _area_epsl_map; }
    const std::unordered_map<int, double> & area_cond_map() const { return _area_cond_map; }
    const std::unordered_map<int, double> & area_mu_map() const { return _area_mu_map; }

    void set_area_aniso_conductivity(const std::unordered_map<int, AnisotropySpecEntry> & m) { _area_aniso_conductivity = m; }
    void set_area_aniso_magnetic(const std::unordered_map<int, AnisotropySpecEntry> & m) { _area_aniso_magnetic = m; }
    void set_area_aniso_dielectric(const std::unordered_map<int, AnisotropySpecEntry> & m) { _area_aniso_dielectric = m; }

    const std::unordered_map<int, AnisotropySpecEntry> & area_aniso_conductivity() const { return _area_aniso_conductivity; }
    const std::unordered_map<int, AnisotropySpecEntry> & area_aniso_magnetic() const { return _area_aniso_magnetic; }
    const std::unordered_map<int, AnisotropySpecEntry> & area_aniso_dielectric() const { return _area_aniso_dielectric; }

    // --------- 合并原 MaterialManager 接口 ---------

    // 函数: initialize
    // 作用: 用给定材料律初始化管理器，并根据系统的活跃单元子域预生成缓存。
    // 参数:
    //   system: 电磁系统（用于扫描网格子域与后续材料查询）
    //   law   : 可选自定义材料律；若为空则使用 LegacyMapMaterialLaw。
    // 返回: 无
    void initialize(const ElectromagneticsSystem & system,
                    std::unique_ptr<MaterialLaw> law = nullptr);

    // 函数: initialize
    // 作用: 从参数读取区域映射与各向异性设置，随后初始化材料律并构建缓存。
    // 参数:
    //   system: 电磁系统（用于扫描网格子域与后续材料查询）
    //   params: 参数读取器（提供区域标量与各向异性规格）
    //   law   : 可选自定义材料律；若为空则使用 LegacyMapMaterialLaw。
    // 返回: 无
    void initialize(const ElectromagneticsSystem & system,
                    const ParameterReader & params,
                    std::unique_ptr<MaterialLaw> law = nullptr);

    // 函数: refresh_cache_for_mesh
    // 作用: 扫描当前处理器持有的活跃单元，收集子域 id，并为每个子域生成/刷新张量缓存。
    // 参数: system - 电磁系统（用于访问网格与参数）
    // 返回: 无
    void refresh_cache_for_mesh(const ElectromagneticsSystem & system);

    // 函数: set_material_law
    // 作用: 更换材料律实现，并刷新缓存。
    // 参数: law - 新的材料律实例（所有权转移）
    // 返回: 无
    void set_material_law(const ElectromagneticsSystem & system,
                          std::unique_ptr<MaterialLaw> law);

    // 函数: get_sigma_tensor / get_epsl_tensor / get_inv_mu_tensor
    // 作用: 读取已缓存的张量（若缓存缺失，请先调用 refresh_cache_for_mesh）。
    // 返回: 3x3 张量的常量引用。
    const std::array<std::array<double, 3>, 3> & get_sigma_tensor(int subdomain_id) const;
    const std::array<std::array<double, 3>, 3> & get_epsl_tensor(int subdomain_id) const;
    const std::array<std::array<double, 3>, 3> & get_inv_mu_tensor(int subdomain_id) const;

    // 函数: has_subdomain
    // 作用: 查询缓存中是否已包含该子域。
    bool has_subdomain(int subdomain_id) const;

private:
    // 名称到材料标量属性的映射表
    std::unordered_map<std::string, Material> _materials;

    // 区域标量材料映射
    std::unordered_map<int, double> _area_epsl_map;
    std::unordered_map<int, double> _area_cond_map;
    std::unordered_map<int, double> _area_mu_map;

    // 各向异性规格映射
    std::unordered_map<int, AnisotropySpecEntry> _area_aniso_conductivity;
    std::unordered_map<int, AnisotropySpecEntry> _area_aniso_magnetic;
    std::unordered_map<int, AnisotropySpecEntry> _area_aniso_dielectric;

    // 当前材料律
    std::unique_ptr<MaterialLaw> _law;

    // 子域到张量的缓存
    std::unordered_map<int, MaterialTensors> _cache;
};

} // namespace cem